///////////////////////////////////////////////////////////////////////////////
//
// DBT86 Project : x86 Platform : x86 Interpreter : Control Flow
// Copyright (C) 2004 by Ben Hertzberg and Stanford University
// All Rights Reserved
//
// This code may not be used for any purpose without the express written
// consent of the author.  See the LICENSE.txt file for details.
//
//
// DESCRIPTION
//
// This unit implements simulation of control flow ops on x86.
//
// TODO:
// - see TODO.txt for additional opcodes needing implementation
//
///////////////////////////////////////////////////////////////////////////////

#include "interp.h"

#define JUMP_REL_WORD_OP(cond, loop_op) { \
    IVAL tmp; \
    CONTROL_POINT; \
    tmp = NEXT_IP + LOAD_IMM; \
    if (cond) { \
      CHECK_VALID_CS_OFFSET(tmp); \
      NEXT_IP = tmp & 0xFFFF; \
    } \
    if (loop_op) { \
      IVAL val; \
      val = LOAD_REG_ECX; \
      val = (val & 0xFFFF) - 1; \
      STORE_REG_ID_WORD(CX,val); \
    } \
  }

#define JUMP_REL_DWORD_OP(cond, loop_op) { \
    IVAL tmp; \
    CONTROL_POINT; \
    tmp = NEXT_IP + LOAD_IMM; \
    if (cond) { \
      CHECK_VALID_CS_OFFSET(tmp); \
      NEXT_IP = tmp; \
    } \
    if (loop_op) { \
      IVAL val; \
      val = LOAD_REG_ECX; \
      val = (val & 0xFFFFFFFF) - 1; \
      STORE_REG_ID_DWORD(ECX,val); \
    } \
  }

#define JUMP_NEAR_WORD_OP(is_rel, is_call, is_ret, has_popcnt) { \
    IVAL tgt,ptr; \
    INT fStackSize; \
    CONTROL_POINT; \
    if (is_ret) { \
      CHECK_STACK_TOP(6); \
      SEG_OVERRIDE(SS); \
      fStackSize = GetSegAttr(SS,SEG_ATTR_B); \
      ptr = LOAD_REG_ESP; \
      ptr = fStackSize ? ptr : ptr & 0xFFFF; \
      LOAD_MEM_WORD(tgt,ptr); \
      ptr += 2; \
      if (has_popcnt) { \
        ptr += (LOAD_IMM); \
      } \
    } else if (is_rel) { \
      tgt = LOAD_IMM; \
      tgt += NEXT_IP; \
    } else { \
      LOAD_RM_R_WORD(tgt); \
    } \
    CHECK_VALID_CS_OFFSET(tgt); \
    if (is_call) { \
      fStackSize = GetSegAttr(SS,SEG_ATTR_B); \
      ptr = LOAD_REG_ESP; \
      ptr = fStackSize ? ptr : ptr & 0xFFFF; \
      SEG_OVERRIDE(SS); \
      ptr -= 2; \
      STORE_MEM_WORD(ptr,NEXT_IP); \
    } \
    if (is_call || is_ret) { \
      if (fStackSize) { \
        STORE_REG_ID_DWORD(ESP,ptr); \
      } else { \
        STORE_REG_ID_WORD(SP,ptr); \
      } \
    } \
    NEXT_IP = tgt & 0xFFFF; \
  }

#define JUMP_NEAR_DWORD_OP(is_rel, is_call, is_ret, has_popcnt) { \
    IVAL tgt,ptr; \
    INT fStackSize; \
    CONTROL_POINT; \
    if (is_ret) { \
      CHECK_STACK_TOP(12); \
      SEG_OVERRIDE(SS); \
      fStackSize = GetSegAttr(SS,SEG_ATTR_B); \
      ptr = LOAD_REG_ESP; \
      ptr = fStackSize ? ptr : ptr & 0xFFFF; \
      LOAD_MEM_DWORD(tgt,ptr); \
      ptr += 4; \
      if (has_popcnt) { \
        ptr += (LOAD_IMM); \
      } \
    } else if (is_rel) { \
      tgt = LOAD_IMM; \
      tgt += NEXT_IP; \
    } else { \
      LOAD_RM_R_DWORD(tgt); \
    } \
    CHECK_VALID_CS_OFFSET(tgt); \
    if (is_call) { \
      fStackSize = GetSegAttr(SS,SEG_ATTR_B); \
      ptr = LOAD_REG_ESP; \
      SEG_OVERRIDE(SS); \
      ptr -= 4; \
      ptr = fStackSize ? ptr : ptr & 0xFFFF; \
      STORE_MEM_DWORD(ptr,NEXT_IP); \
    } \
    if (is_call || is_ret) { \
      if (fStackSize) { \
        STORE_REG_ID_DWORD(ESP,ptr); \
      } else { \
        STORE_REG_ID_WORD(SP,ptr); \
      } \
    } \
    NEXT_IP = tgt; \
  }

void JO_Jd()   { JUMP_REL_DWORD_OP(  COND_EVAL_O   ,0); }
void JNO_Jd()  { JUMP_REL_DWORD_OP(!(COND_EVAL_O ) ,0); }
void JB_Jd()   { JUMP_REL_DWORD_OP(  COND_EVAL_B   ,0); }
void JNB_Jd()  { JUMP_REL_DWORD_OP(!(COND_EVAL_B ) ,0); }
void JE_Jd()   { JUMP_REL_DWORD_OP(  COND_EVAL_E   ,0); }
void JNE_Jd()  { JUMP_REL_DWORD_OP(!(COND_EVAL_E ) ,0); }
void JBE_Jd()  { JUMP_REL_DWORD_OP(  COND_EVAL_BE  ,0); }
void JNBE_Jd() { JUMP_REL_DWORD_OP(!(COND_EVAL_BE) ,0); }
void JS_Jd()   { JUMP_REL_DWORD_OP(  COND_EVAL_S   ,0); }
void JNS_Jd()  { JUMP_REL_DWORD_OP(!(COND_EVAL_S ) ,0); }
void JP_Jd()   { JUMP_REL_DWORD_OP(  COND_EVAL_P   ,0); }
void JNP_Jd()  { JUMP_REL_DWORD_OP(!(COND_EVAL_P ) ,0); }
void JL_Jd()   { JUMP_REL_DWORD_OP(  COND_EVAL_L   ,0); }
void JNL_Jd()  { JUMP_REL_DWORD_OP(!(COND_EVAL_L ) ,0); }
void JLE_Jd()  { JUMP_REL_DWORD_OP(  COND_EVAL_LE  ,0); }
void JNLE_Jd() { JUMP_REL_DWORD_OP(!(COND_EVAL_LE) ,0); }

void JO_Jw()   { JUMP_REL_WORD_OP(  COND_EVAL_O   ,0); }
void JNO_Jw()  { JUMP_REL_WORD_OP(!(COND_EVAL_O ) ,0); }
void JB_Jw()   { JUMP_REL_WORD_OP(  COND_EVAL_B   ,0); }
void JNB_Jw()  { JUMP_REL_WORD_OP(!(COND_EVAL_B ) ,0); }
void JE_Jw()   { JUMP_REL_WORD_OP(  COND_EVAL_E   ,0); }
void JNE_Jw()  { JUMP_REL_WORD_OP(!(COND_EVAL_E ) ,0); }
void JBE_Jw()  { JUMP_REL_WORD_OP(  COND_EVAL_BE  ,0); }
void JNBE_Jw() { JUMP_REL_WORD_OP(!(COND_EVAL_BE) ,0); }
void JS_Jw()   { JUMP_REL_WORD_OP(  COND_EVAL_S   ,0); }
void JNS_Jw()  { JUMP_REL_WORD_OP(!(COND_EVAL_S ) ,0); }
void JP_Jw()   { JUMP_REL_WORD_OP(  COND_EVAL_P   ,0); }
void JNP_Jw()  { JUMP_REL_WORD_OP(!(COND_EVAL_P ) ,0); }
void JL_Jw()   { JUMP_REL_WORD_OP(  COND_EVAL_L   ,0); }
void JNL_Jw()  { JUMP_REL_WORD_OP(!(COND_EVAL_L ) ,0); }
void JLE_Jw()  { JUMP_REL_WORD_OP(  COND_EVAL_LE  ,0); }
void JNLE_Jw() { JUMP_REL_WORD_OP(!(COND_EVAL_LE) ,0); }

void JCXZ()    { JUMP_REL_WORD_OP(((LOAD_REG_ECX & 0xFFFF) == 0), 0); }
void JECXZ()   { JUMP_REL_DWORD_OP(((LOAD_REG_ECX & 0xFFFFFFFF) == 0), 0); }
void LOOP()    { JUMP_REL_WORD_OP(((LOAD_REG_ECX & 0xFFFF) != 0), 1); }
void LOOPD()   { JUMP_REL_DWORD_OP(((LOAD_REG_ECX & 0xFFFFFFFF) != 0), 1); }
void LOOPE()   { JUMP_REL_WORD_OP((((LOAD_REG_ECX & 0xFFFF) != 0) && COND_EVAL_E), 1); }
void LOOPDE()  { JUMP_REL_DWORD_OP((((LOAD_REG_ECX & 0xFFFFFFFF) != 0) && COND_EVAL_E), 1); }
void LOOPNE()  { JUMP_REL_WORD_OP((((LOAD_REG_ECX & 0xFFFF) != 0) && !(COND_EVAL_E)), 1); }
void LOOPDNE() { JUMP_REL_DWORD_OP((((LOAD_REG_ECX & 0xFFFFFFFF) != 0) && !(COND_EVAL_E)), 1); }

void JMPN_Jw()  { JUMP_NEAR_WORD_OP(1,0,0,0); }
void JMPN_Jd()  { JUMP_NEAR_DWORD_OP(1,0,0,0); }
void JMPN_Ew()  { JUMP_NEAR_WORD_OP(0,0,0,0); }
void JMPN_Ed()  { JUMP_NEAR_DWORD_OP(0,0,0,0); }

void CALLN_Jw() { JUMP_NEAR_WORD_OP(1,1,0,0); }
void CALLN_Jd() { JUMP_NEAR_DWORD_OP(1,1,0,0); }
void CALLN_Ew() { JUMP_NEAR_WORD_OP(0,1,0,0); }
void CALLN_Ed() { JUMP_NEAR_DWORD_OP(0,1,0,0); }

void RETN()     { JUMP_NEAR_WORD_OP(0,0,1,0); }
void RETND()    { JUMP_NEAR_DWORD_OP(0,0,1,0); }
void RETN_Iw()  { JUMP_NEAR_WORD_OP(0,0,1,1); }
void RETND_Iw() { JUMP_NEAR_DWORD_OP(0,0,1,1); }

// TODO: these ops
void CALLF_w_w() { ASSERT(UNREACHED); }
void CALLF_d_w() { ASSERT(UNREACHED); }
void RETF_Iw() { ASSERT(UNREACHED); }
void RETF() { ASSERT(UNREACHED); }
void IRET() { ASSERT(UNREACHED); }
void IRETD() { ASSERT(UNREACHED); }
void JMPf_APw() { ASSERT(UNREACHED); }
void JMPf_APd() { ASSERT(UNREACHED); }
void CALLF_Ep() { ASSERT(UNREACHED); }
void JMPF_Ep() { ASSERT(UNREACHED); }
